 #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Management script."""
import os
from glob import glob
from subprocess import call
from celery import Celery
from flask import request, jsonify

from flask_migrate import Migrate, MigrateCommand
from flask_script import Command, Manager, Option, Server, Shell
from flask_script.commands import Clean, ShowUrls

from AmoluoSale.app import create_app
from AmoluoSale.database import db
from AmoluoSale.settings import DevConfig, ProdConfig
from AmoluoSale.user.models import User
from AmoluoSale.item.models import Item, Reserve_Order
#CONFIG = ProdConfig if os.environ.get('AMOLUOSALE_ENV') == 'prod' else DevConfig
CONFIG = ProdConfig
HERE = os.path.abspath(os.path.dirname(__file__))

def make_celery(app):
    celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],
                    broker=app.config['BROKER_URL'])
    celery.conf.update(app.config)
    TaskBase = celery.Task
    class ContextTask(TaskBase):
        abstract = True
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)
    celery.Task = ContextTask
    return celery

app = create_app(CONFIG)
manager = Manager(app)
migrate = Migrate(app, db)
celery = make_celery(app)

@celery.task
def holdItem(itemid, quantity):
    item = Item.query.get(itemid)
    item.sold += quantity
    if item.total < item.sold:
        return False
    else: # update db only when it is a vaild order
        db.session.commit()
        release_task = releaseItem.apply_async(args=[itemid, quantity], countdown=5*60)
        # store release_task.id in redis, give it to frontend
        # when order has been successfully created, revoke the task
        return release_task.id

@celery.task
def releaseItem(itemid, quantity):
    item = Item.query.get(itemid)
    item.sold -= quantity
    db.session.commit()
    return True

@celery.task
def revokeReleaseTask(task_id):
    celery.control.revoke(task_id)

@celery.task
def checkTaskStatus(task_id):
    return celery.AsyncResult(task_id).state

@app.route("/holdItem", methods=['GET','POST'])
def holdOrderItem():
    openid = request.values.get('openid', '')
    item_id = request.values.get('itemid', '')
    quantity = int(request.values.get('quantity', '0'))
    # hold item
    result = holdItem.delay(item_id, quantity)
    ro = Reserve_Order()
    ro.openid = openid
    ro.item_id = item_id
    ro.quantity = quantity
    ro.task_id = result.get()
    db.session.add(ro)
    db.session.commit()
    return jsonify(result = result.get())


@app.route("/releaseItem", methods=['GET','POST'])
def releaseOrderItem():
    item_id = request.values.get('itemid', '')
    quantity = int(request.values.get('quantity', '0'))
    # release pre-hold item
    result = releaseItem.delay(item_id, quantity)
    return jsonify(result = result.get())

@app.route("/revokeTask", methods=['GET','POST'])
def revokeTask():
    task_id = request.values.get('task_id', '')
    result = revokeReleaseTask.delay(task_id)
    return jsonify(result = result.get())

@app.route("/checkTask", methods=['GET','POST'])
def checkTask():
    task_id = request.values.get('task_id', '')
    result = checkTaskStatus.delay(task_id)
    return jsonify(result = result.get())


def _make_context():
    """Return context dict for a shell session so you can access app, db, and the User model by default."""
    return {'app': app, 'db': db, 'User': User}



class Lint(Command):
    """Lint and check code style with flake8 and isort."""

    def get_options(self):
        """Command line options."""
        return (
            Option('-f', '--fix-imports', action='store_true', dest='fix_imports', default=False,
                   help='Fix imports using isort, before linting'),
        )

    def run(self, fix_imports):
        """Run command."""
        skip = ['requirements']
        root_files = glob('*.py')
        root_directories = [name for name in next(os.walk('.'))[1] if not name.startswith('.')]
        files_and_directories = [arg for arg in root_files + root_directories if arg not in skip]

        def execute_tool(description, *args):
            """Execute a checking tool with its arguments."""
            command_line = list(args) + files_and_directories
            print('{}: {}'.format(description, ' '.join(command_line)))
            rv = call(command_line)
            if rv is not 0:
                exit(rv)

        if fix_imports:
            execute_tool('Fixing import order', 'isort', '-rc')
        execute_tool('Checking code style', 'flake8')


manager.add_command('server', Server())
manager.add_command('shell', Shell(make_context=_make_context))
manager.add_command('db', MigrateCommand)
manager.add_command('urls', ShowUrls())
manager.add_command('clean', Clean())
manager.add_command('lint', Lint())

if __name__ == '__main__':
    manager.run()
